/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package machine;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.br.BrazilianAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.util.Version;
import org.jsoup.Jsoup;

/**
 *
 * @author Guilherme
 */
public class QueryProcessor {

    private String query;
    private double K;
    private double B;
    private double avgDocLen;
    private int N;

    private BrazilianAnalyzer brazilianAnalyzer;

    private List<Pair> queryList;

    private static String[] stopWords = {"a", "agora", "ainda", "alguém", "algum",
        "alguma", "algumas", "alguns", "ampla", "amplas", "amplo", "amplos", "ante",
        "antes", "ao", "aos", "após", "aquela", "aquelas", "aquele", "aqueles", "aquilo",
        "as", "até", "através", "cada", "coisa", "coisas", "com", "como", "contra",
        "contudo", "da", "daquele", "daqueles", "das", "de", "dela", "delas", "dele",
        "deles", "depois", "dessa", "dessas", "desse", "desses", "desta", "destas",
        "deste", "deste", "destes", "deve", "devem", "devendo", "dever", "deverá",
        "deverão", "deveria", "deveriam", "devia", "deviam", "disse", "disso",
        "disto", "dito", "diz", "dizem", "do", "dos", "e", "é", "ela", "elas", "ele",
        "eles", "em", "enquanto", "entre", "era", "essa", "essas", "esse", "esses",
        "esta", "está", "estamos", "estão", "estas", "estava", "estavam", "estávamos",
        "este", "estes", "estou", "eu", "fazendo", "fazer", "feita", "feitas",
        "feito", "feitos", "foi", "for", "foram", "fosse", "fossem", "grande",
        "grandes", "há", "isso", "isto", "já", "la", "lá", "lhe", "lhes", "lo",
        "mas", "me", "mesma", "mesmas", "mesmo", "mesmos", "meu", "meus", "minha",
        "minhas", "muita", "muitas", "muito", "muitos", "na", "não", "nas", "nem",
        "nenhum", "nessa", "nessas", "nesta", "nestas", "ninguém", "no", "nos", "nós",
        "nossa", "nossas", "nosso", "nossos", "num", "numa", "nunca", "o", "os", "ou",
        "outra", "outras", "outro", "outros", "para", "pela", "pelas", "pelo", "pelos",
        "pequena", "pequenas", "pequeno", "pequenos", "per", "perante", "pode",
        "pude", "podendo", "poder", "poderia", "poderiam", "podia", "podiam", "pois",
        "por", "porém", "porque", "posso", "pouca", "poucas", "pouco", "poucos",
        "primeiro", "primeiros", "própria", "próprias", "próprio", "próprios", "quais",
        "qual", "quando", "quanto", "quantos", "que", "quem", "são", "se", "seja",
        "sejam", "sem", "sempre", "sendo", "será", "serão", "seu", "seus", "si", "sido",
        "só", "sob", "sobre", "sua", "suas", "talvez", "também", "tampouco", "te",
        "tem", "tendo", "tenha", "ter", "teu", "teus", "ti", "tido", "tinha",
        "tinham", "toda", "todas", "todavia", "todo", "todos", "tu", "tua", "tuas",
        "tudo", "última", "últimas", "último", "últimos", "um", "uma", "umas",
        "uns", "vendo", "ver", "vez", "vindo", "vir", "vos", "vós"};

    public QueryProcessor(double K, double B, int docLen, double avgDocLen) {
        this.K = K;
        this.B = B;
        this.avgDocLen = avgDocLen;
        this.N = docLen;
        this.queryList = new ArrayList<Pair>();
        this.brazilianAnalyzer = new BrazilianAnalyzer(Version.LUCENE_35, this.stopWords);
    }

    public List getQueryList() {
        return this.queryList;
    }

    public void process(String query) {

        query = query.toLowerCase()//Passa tudo para minúsculo
                .replaceAll("\\p{Punct}", " ");//Remove toda a pontuação (menos números)
        List<String> tokens = tokenizeString(brazilianAnalyzer, query);

        MongoDB db = new MongoDB();
        db.connect();

        /**
         * Lista de Token [documentID, frequency] para cada token da query, cria
         * um hash com <tokenID, lista invertida>
         * lista invertida contém a idDocument e a frequencyDocument
         */
        HashMap<Long, HashMap<Long, Integer>> tokenList = new HashMap();

        for (String token : tokens) {
            Long tokenID = db.getTokenID(token);
            if (tokenID > -1) { //não existe token no banco de dados
                tokenList.put(tokenID, db.getTokenProperties(tokenID));
            }
        }

        ArrayList<Long> docTokensList = new ArrayList<Long>();
        //cria uma lista e documentos que aparecem na consulta
        for (Long tokenID : tokenList.keySet()) {
            HashMap<Long, Integer> invertedList = tokenList.get(tokenID);
            for (Long documentID : invertedList.keySet()) {
                docTokensList.add(documentID);
            }
        }

        //cria uma lista de documentos que aparecem na consulta com similaridade
        HashMap<Long, Double> simsDocuments = new HashMap<Long, Double>();
        double sum = .0;
        for (Long documentID : docTokensList) {

            for (Long tokenID : tokenList.keySet()) {
                Integer frequency = tokenList.get(tokenID).get(documentID);

                if (frequency != null) {
                    sum += bij(frequency, db.getDocumentLenght(documentID))
                            * Math.log((this.N - frequency + 0.5)
                                    / (frequency + 0.5)) / Math.log(2);
                }
            }

            simsDocuments.put(documentID, sum);
            sum = .0;
        }
        docTokensList = null;
        System.gc();

        //ordena a lista
        List<Pair> simsDocumentsList = new ArrayList<Pair>();

        for (Long documentID : simsDocuments.keySet()) {
            Pair pair = new Pair(documentID, simsDocuments.get(documentID));
            simsDocumentsList.add(pair);
        }

        Collections.sort(simsDocumentsList);

        this.queryList = simsDocumentsList;

    }

    public Double bij(Integer frequency, int documentLength) {
        return ((this.K + 1) * frequency) / (this.K * ((1 - this.B) + this.B
                * documentLength / this.avgDocLen) + frequency);
    }

    private List<String> tokenizeString(Analyzer analyzer, String string) {
        List<String> result = new ArrayList<String>();
        try {
            TokenStream stream = analyzer.tokenStream(null, new StringReader(string));
            stream.reset();
            while (stream.incrementToken()) {
                result.add(stream.getAttribute(CharTermAttribute.class).toString());
            }
        } catch (IOException e) {
            // not thrown b/c we're using a string reader...
            System.err.print("[ERRO] Erro ao realizar a tokenização!");
        }
        return result;
    }

}
